package com.SinhalaSpellChecker;

import View.ErrorDialog;
import com.sun.star.beans.PropertyValue;
import com.sun.star.beans.XPropertySet;
import com.sun.star.lang.Locale;
import com.sun.star.uno.XComponentContext;
import com.sun.star.lib.uno.helper.Factory;
import com.sun.star.lang.XSingleComponentFactory;
import com.sun.star.registry.XRegistryKey;
import com.sun.star.lib.uno.helper.ComponentBase;
import com.sun.star.linguistic2.SpellFailure;
import com.sun.star.linguistic2.XSpellAlternatives;
import com.sun.star.linguistic2.XSpellChecker;
import com.sun.star.uno.AnyConverter;
import com.sun.star.uno.UnoRuntime;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.TreeMap;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;


public final class SinhalaSpellChecker extends ComponentBase
   implements com.sun.star.lang.XServiceInfo,
              com.sun.star.linguistic2.XSpellChecker,
              com.sun.star.linguistic2.XLinguServiceEventBroadcaster,
              com.sun.star.lang.XInitialization,
              com.sun.star.lang.XServiceDisplayName
{
    private final XComponentContext m_xContext;
    private static final String m_implementationName = SinhalaSpellChecker.class.getName();
    private static final String[] m_serviceNames = {"com.sun.star.linguistic2.SpellChecker" };
    private PropChgHelper_Spell aPropChgHelper;
    private ArrayList aEvtListeners;
    private boolean bDisposing;
    private WordListHandler handler;
    private TreeMap<String, ArrayList> map;
    private Locale locale=new Locale( "si", "LK", "" );

    /**
     * Class constructor which initializes the XComponentContext and class variables aProps,
     * aPropChgHelper, aEvtListeners, bDisposing,   handler.
     * @param  contect XComponentContext obejct
     */
    public SinhalaSpellChecker( XComponentContext context )
    {
        m_xContext = context;
        String[] aProps = new String[]{"IsSinhala"};
        aPropChgHelper  = new PropChgHelper_Spell( (XSpellChecker) this, aProps );
        aEvtListeners   = new ArrayList();
        bDisposing      = false;
        handler=WordListHandler.getHandler();
    };

    /*Generated by the OpenOffice plug-in in net beans*/
    public static XSingleComponentFactory __getComponentFactory( String sImplementationName ) {
        XSingleComponentFactory xFactory = null;

        if ( sImplementationName.equals( m_implementationName ) )
            xFactory = Factory.createComponentFactory(SinhalaSpellChecker.class, m_serviceNames);
        return xFactory;
    }

    /*Generated by the OpenOffice plug-in in net beans*/
    public static boolean __writeRegistryServiceInfo( XRegistryKey xRegistryKey ) {
        return Factory.writeRegistryServiceInfo(m_implementationName,
                                                m_serviceNames,
                                                xRegistryKey);
    }

    // com.sun.star.lang.XServiceInfo:
    /*Generated by the OpenOffice plug-in in net beans*/
    /**
     * Return the implementation name of this project
     */
    public String getImplementationName() {
         return m_implementationName;
    }

    /*Generated by the OpenOffice plug-in in net beans*/
    /**
     * Return if the given service is supported.
     * @param aService Service name
     * @return boolean value
     */
    public boolean supportsService( String sService ) {
        int len = m_serviceNames.length;

        for( int i=0; i < len; i++) {
            if (sService.equals(m_serviceNames[i]))
                return true;
        }
        return false;
    }

    /*Generated by the OpenOffice plug-in in net beans*/
    /**
     * Return the string array containing the service names
     * @return  the string array of service names
     */
    public String[] getSupportedServiceNames() {
        return m_serviceNames;
    }

    // com.sun.star.linguistic2.XSupportedLocales:
    /*Gives the locale that is used in the spell checker*/
    /**
     * Returns an array containing the locales supported by the spell checker
     * @return array of com.sun.star.lang.Locale
     */
    public com.sun.star.lang.Locale[] getLocales()
    {
        Locale aLocales[] ={locale};
        return aLocales;
    }

    /*check if the given locale is the correct locale*/
    /**
     * Check if the given locale is supported by the spell checker
     * @param aLocale com.sun.star.lang.Locale object that needs to be checked
     * @return boolean value
     */
    public boolean hasLocale(com.sun.star.lang.Locale aLocale)
    {
        boolean bRes = false;
        if ( IsEqual( aLocale, locale ))
            bRes = true;
        return bRes;
    }

    // com.sun.star.linguistic2.XSpellChecker:
     /**
     * check if the word is valid according to the locale and property and define the type of faliure to
     * be diplayed to the user.
      * @param  aWord the input by the user through OpenOffice
      * @param  aLocale com.sun.star.lang.Locale supported by the spell checker
      * @param  aProperties array of com.sun.star.beans.PropertyValue
      * @return boolean value whether the word is spelled correctly or not
      * @throws com.sun.star.lang.IllegalArgumentException
     */
    public boolean isValid(String aWord, com.sun.star.lang.Locale aLocale, com.sun.star.beans.PropertyValue[] aProperties) throws com.sun.star.lang.IllegalArgumentException
    {
         if (IsEqual( aLocale, new Locale() ) || aWord.length() == 0) //check if the word does not have any letters or does not match with the locale
            return true;

         if (!hasLocale( aLocale )) //check if the word
            return true;

         boolean bSinhala      = GetValueToUse( "IsSinhala", false, aProperties );

        short nFailure = GetSpellFailure( aWord, aLocale, aProperties );  //get the type of faliure
        if (nFailure != -1)
        {
            if (bSinhala)
                nFailure = -1;
        }

        return nFailure == -1;
    }

     /*Return the suggestions to be displayed to OpenOffice display*/
    /**
     * Return the suggestions to be displayed to OpenOffice display.
     * @param aWrod the input by the user through OpenOffice
     * @param  aLocale com.sun.star.lang.Locale supported by the spell checker
     * @param  aProperties array of com.sun.star.beans.PropertyValue
     * @throws com.sun.star.lang.IllegalArgumentException
     * @see #GetProposals(java.lang.String, com.sun.star.lang.Locale, com.sun.star.beans.PropertyValue[])
     */
    public com.sun.star.linguistic2.XSpellAlternatives spell(String aWord, com.sun.star.lang.Locale aLocale, com.sun.star.beans.PropertyValue[] aProperties) throws com.sun.star.lang.IllegalArgumentException
    {
          if (IsEqual( aLocale, new Locale() ) || aWord.length() == 0)
            return null;

       if (!hasLocale( aLocale ))
            return null;

        XSpellAlternatives xRes = null;
        if (!isValid( aWord, aLocale, aProperties ))
        {
            xRes = GetProposals( aWord, aLocale, aProperties );
        }
        return xRes;
    }

    // com.sun.star.linguistic2.XLinguServiceEventBroadcaster:
    /**
     * Add the linguistic service listners to the properties.
     * @param xLstnr (com.sun.star.linguistic2.XLinguServiceEventListener object
     * @return boolean value
     * @see PropChgHelper
     */
    public boolean addLinguServiceEventListener(com.sun.star.linguistic2.XLinguServiceEventListener xLstnr)
    {
        boolean bRes = false;
        if (!bDisposing && xLstnr != null)
            bRes = aPropChgHelper.addLinguServiceEventListener( xLstnr );
        return bRes;
    }

    /**
     * Remove the linguistic service listners to the properties.
     * @param xLstnr (com.sun.star.linguistic2.XLinguServiceEventListener object
     * @return boolean value
     * @see PropChgHelper
     */
    public boolean removeLinguServiceEventListener(com.sun.star.linguistic2.XLinguServiceEventListener xLstnr)
    {
        boolean bRes = false;
        if (!bDisposing && xLstnr != null)
            bRes = aPropChgHelper.removeLinguServiceEventListener( xLstnr );
        return bRes;
    }

    // com.sun.star.lang.XInitialization:
    /**
     * Initialize the spell checker add listners to the property set.
     * @param aArguments an array of Objects
     * @throws com.sun.star.uno.Exception
     * @see  UnoRuntime
     * @see PropChgHelper
     */
    public void initialize(Object[] aArguments) throws com.sun.star.uno.Exception
    {
       int nLen = aArguments.length;
        if (2 == nLen)
        {
            XPropertySet xPropSet = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class, aArguments[0]);
            aPropChgHelper.AddAsListenerTo( xPropSet );
        }
    }

    // com.sun.star.lang.XServiceDisplayName:
    /**
     * define the display name of the spell checker
     * @param aLocale com.sun.star.lang.Locale aLocale
     * @return display name of the spell checker
     */
    public String getServiceDisplayName(com.sun.star.lang.Locale aLocale)
    {
        return "Sinhala Spell Checker";
    }

    /**
     * check if two locales are equal
     * @param aLoc1 locale 1
     * @param aLoc2 locale 2
     * @return boolean value
     */
    private boolean IsEqual( Locale aLoc1, Locale aLoc2 )
    {
        return aLoc1.Language.equals( aLoc2.Language ) && aLoc1.Country .equals( aLoc2.Country )  && aLoc1.Variant .equals( aLoc2.Variant );
    }

    /**
     * Get the values of the properties
     * @param aPropName name of the property
     * @param bDefaultVal the default value for the boolean
     * @param aProps an array of PropertyValue objects
     * @return the boolean value
     */
    private boolean GetValueToUse(String aPropName,  boolean bDefaultVal, PropertyValue[] aProps )
    {
        boolean bRes = bDefaultVal;

        try
        {
            for (int i = 0;  i < aProps.length;  ++i)
            {
                if (aPropName.equals( aProps[i].Name ))
                {
                    Object aObj = aProps[i].Value;
                    if (AnyConverter.isBoolean( aObj ))
                    {
                        bRes = AnyConverter.toBoolean( aObj );
                        return bRes;
                    }
                }
            }
            XPropertySet xPropSet = aPropChgHelper.GetPropSet();
            if (xPropSet != null)
            {
                Object aObj = xPropSet.getPropertyValue( aPropName );
                if (AnyConverter.isBoolean( aObj ))
                    bRes = AnyConverter.toBoolean( aObj );
            }
        }
        catch (Exception e) {
            bRes = bDefaultVal;
        }

        return bRes;
    }

    /**
     * Compare with word list in the file and compare the word with words in the file.
     * If there is a match then the word is spelled correctly. If no match is found then it
     * is assumed that the word is misspelled.
     * @param aWord the input by the user through OpenOffice
     * @param aLocale the locale supported by the spell checker
     * @param aProperties an array of PropertyValues
     * @return a short value indicating the type of faliure
     */
    private short GetSpellFailure(String aWord, Locale aLocale,  PropertyValue[] aProperties )   //define the word as spell faliure if it does not match with the list
    {
        short nRes = -1;
        boolean bIsSinhala     = GetValueToUse( "IsSinhala", false, aProperties );
        if (IsEqual( aLocale, locale ))
        {
           try{
               if(map==null){
                    map=handler.readWordFile();        //get the from the file in to TreeMap
               }
                boolean isExist=handler.isExist(map, aWord);          //check if the word exist in the word list
                if(!isExist&&!bIsSinhala){                              //If the word is not in the wordList
                    nRes=SpellFailure.SPELLING_ERROR;
                }
            } catch (UnsupportedEncodingException ex) {
                String errorMessage="The encoding is unsupported. Please use UNICODE.";
                displayErrorMessage(errorMessage);
            } catch (ClassNotFoundException ex) {
                String errorMessage="Error while reading from the dctionary file.";
                displayErrorMessage(errorMessage);
            }catch(FileNotFoundException e){                    //If the file that contains word list does not exist
                String errorMessage="Dictionary file does not exist.";
                displayErrorMessage(errorMessage);
            }catch(IOException e){                              //If an error occurs while reading from the file
                String errorMessage="Error while reading from the dctionary file.";
                displayErrorMessage(errorMessage);
            }
        }


        return nRes;
    }

    //
    /**
     * This method passes the suggestion for the misspelled word to OpenOffice display
     * @param aWord the input by the user through OpenOffice
     * @param aLocale the locale supported by the spell checker
     * @param aProperties an array of PropertyValues
     * @returns  XSpellAlternatives_impl object
     * @see XSpellAlternatives
     * @see XSpellAlternatives_impl
     */
    private XSpellAlternatives GetProposals(String aWord,Locale aLocale,PropertyValue[] aProperties )   //Suggest alternatives from the list
    {
        short    nType = SpellFailure.SPELLING_ERROR;           //define the error type as spelling error
        String[] aProposals = null;
        boolean bIsSinhala      = GetValueToUse( "IsSinhala", false, aProperties );
        if (IsEqual( aLocale, locale ))
        {
            aProposals=handler.identifySuggestions(map, aWord);         //identify suggestions based on the given word from the map
        }
        return new XSpellAlternatives_impl( aWord, aLocale, nType, aProposals );
    }

    /**
     * Display the error messages to the user.
     * @param errorMessage the message to be displayed to the useer
     * @see ErrorDialog
     */
    private void displayErrorMessage(String errorMessage){
         String path = SinhalaSpellChecker.class.getProtectionDomain().getCodeSource().getLocation().getPath();
         path = path.replace("/", "\\");     //the forward slashes are replace by double back slashes to the path
         path = path.replaceFirst("\\\\", ""); //the first occurence of /// is removed
         path = path.replace("%20", " "); //replace the space
         path = path.replace("SinhalaSpellChecker.jar", "SinhalaSpellChecker.jar/View/error icon.jpg");//The file name of the word list is added to the path by replacing the name of the jar file with the file name
         JFrame frame = new JFrame();
         frame.setIconImage(new ImageIcon(path).getImage());
         ErrorDialog dialog=new ErrorDialog(frame, true);
         dialog.setMessage(errorMessage);           //set the error message to the user
         dialog.setVisible(true);                   //make the dialog visible to the user
    }

}
